package com.editso.haier.storage;

import com.editso.haier.util.Pair;
import com.editso.haier.util.Utils;
import com.editso.haier.util.XUri;
import io.minio.MinioClient;
import io.minio.ObjectStat;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.*;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.util.List;


@ConditionalOnExpression(value = "'${haier.resource-storage-type:local}' == 'minio'")
@Component
public class MinioStorage implements Storage {

    private MinioClient minio;

    private XUri xUri;

    @Value("${haier.resource-storage-uri}")
    private String uri;

    @Value("${minio.assetsKey:uri}")
    private String assetsKey;

    @Value("${minio.secretKey:uri}")
    private String secretKey;

    @Value("${minio.defaultBucket:assets}")
    private String defaultBucket;

    @PostConstruct
    public void created() {
        xUri = XUri.parse(uri);
        try {
            if ("uri".equals(assetsKey)) assetsKey = xUri.getQuery("assetsKey");
            if ("uri".equals(secretKey)) secretKey = xUri.getQuery("secretKey");
            this.minio = new MinioClient(
                    String.format("%s://%s:%d", xUri.getScheme(), xUri.getHost(), xUri.getPort()),
                    assetsKey,
                    secretKey
            );
            if (!this.minio.bucketExists(defaultBucket)) {
                this.minio.makeBucket(defaultBucket);
            }
        } catch (Exception e) {
            throw new Error("存储服务连接失败!", e);
        }
    }

    /**
     * 根uri
     */
    @Override
    public URI getUri() {
        return xUri.getUri();
    }

    @Override
    public String getScheme() {
        return xUri.getScheme();
    }


    private Pair<String, String> bucket(String path, String fileName) {
        if (File.separator.equals(path)) path = null;
        path = path == null ? defaultBucket : path;
        String[] arr = path.split(File.separator);
        List<String> strings = Utils.arrayRange(arr, 1, arr.length);
        strings.add(fileName);
        return new Pair<>(arr[0], Utils.joinPath(strings.toArray(new String[0])));
    }

    @Override
    public String save(String path, String fileName, InputStream stream, String contentType) throws IOException {
        Pair<String, String> bucket = bucket(path, fileName);
        try {
            if (!minio.bucketExists(bucket.getKey()))
                minio.makeBucket(bucket.getKey());
            this.minio.putObject(
                    bucket.getKey(),
                    bucket.get(),
                    stream,
                    contentType == null ? "*/*" : contentType);
        } catch (Exception e) {
            throw new IOException(e);
        }
        return fileName;
    }


    @Override
    public FileItem get(String path, String fileName) throws IOException {
        Pair<String,String> bucket = bucket(path, fileName);
        try {
            return new MinioFileItem(minio.statObject(bucket.getKey(), bucket.get()));
        } catch (Exception e) {
            throw new IOException(e);
        }
    }

    @Override
    public boolean delete(String path, String fileName) {

        return false;
    }

    @Override
    public boolean update(String path, String fileName, InputStream stream) {

        return false;
    }

    @Override
    public boolean exists(String path, String fileName) {
        Pair<String, String> bucket = bucket(path, fileName);
        try {
            if (!minio.bucketExists(bucket.getKey()))
                return false;
            minio.statObject(bucket.getKey(), bucket.get());
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    @Override
    public List<FileItem> fileList(String path) throws IOException {
        if (path == null || File.separator.equals(path))
            path = null;
        // TODO
        return null;
    }

    private final class MinioFileItem implements FileItem {
        private final ObjectStat stat;

        public MinioFileItem(ObjectStat stat) {
            this.stat = stat;
        }

        @Override
        public boolean isDirectory() {
            return false;
        }

        @Override
        public boolean isFile() {
            return true;
        }

        @Override
        public String getName() {
            return stat.name();
        }

        @Override
        public String getPath() {
            return stat.name();
        }

        @Override
        public String getContentType() {
            return stat.contentType();
        }

        @Override
        public long getLength() {
            return stat.length();
        }

        @Override
        public InputStream stream() throws FileNotFoundException {
            try {
                return minio.getObject(stat.bucketName(), stat.name());
            } catch (Exception e) {
                throw new FileNotFoundException(stat.name());
            }
        }
    }
}
